#include "ipcam.h"
#include "h264dec.h"
#include "protocol.h"

#include <netdb.h>
#include <stdio.h>
#include <unistd.h>
#include <time.h>

#if 0
#define MAX_BUFFER_SIZE			(704 * 576 * 2)
#else
#define MAX_BUFFER_SIZE			(1280 * 720 * 2)
#endif

CIpCam::CIpCam()
{
	memset(m_szIpAddr, 0, sizeof(m_szIpAddr));
	memset(m_szUserName, 0, sizeof(m_szUserName));
	memset(m_szPassword, 0, sizeof(m_szPassword));
	
	strcpy(m_szIpAddr, "192.168.0.21");
	m_iCmdPort = 60001;
	m_iMdaPort = 60002;
	strcpy(m_szUserName, "admin");
	strcpy(m_szPassword, "123456");
	
	m_poCmdJavaVM 		= NULL;
	m_objCmdInterface 	= NULL;
	m_pCmdEnv			= NULL;
	m_bIsCmdAttached 	= FALSE;
	m_jmidProcess		= NULL;
	m_objCmdCallBack	= NULL;
	
	m_poMdaJavaVM 		= NULL;
	m_objMdaInterface 	= NULL;
	m_pMdaEnv			= NULL;
	m_bIsMdaAttached 	= FALSE;
	m_jmidProcVideoData	= NULL;
	m_jmidProcAudioData	= NULL;
	m_objMdaCallBack	= NULL;
	
	m_oCmdClient.IRLAB_RegisterCallback(1, IRLAB_ClientRecvData, this);
	m_oMdaClient.IRLAB_RegisterCallback(0, IRLAB_ClientRecvData, this);
	m_bIsDisplaying		= FALSE;
	
	sem_init(&m_semaphore, 0, 0);
	m_bIsLogined		= FALSE;
	m_bIsProcessing		= FALSE;
	m_nSessionID		= 0;
	m_iHBCount			= 0;
	m_iWaitTime			= 0;
	m_bIsVideoOpened	= FALSE;
	m_bIsAudioOpened	= FALSE;
	m_bIsMainStream		= FALSE;
	m_nResult			= 0;
	m_bIsAutoLogin		= FALSE;
	
	m_szVideoData 		= new unsigned char[800 * 1024];
	m_szAudioData		= new unsigned char[1 * 1024];
	LOGI("CIpCam::CIpCam(%x) m_szVideoData %x m_szAudioData %x", this, m_szVideoData, m_szAudioData); 
	memset(m_szVideoData, 0, 800 * 1024);
	memset(m_szAudioData, 0, 1 * 1024);
	m_dwVideoFrameLen	= 0;
	m_dwAudioFrameLen	= 0;
	
	sem_init(&m_semDisplay, 0, 0);
	for (int i=0; i<MAX_RGB16_DATA_NUM; i++)
	{
		m_stData[i].byIsValid = 0;
		m_stData[i].nWidth = 0;
		m_stData[i].nHeight = 0;
		m_stData[i].pszRGBData = new unsigned char[MAX_BUFFER_SIZE];
		memset(m_stData[i].pszRGBData, 0, MAX_BUFFER_SIZE);
		LOGI("CIpCam::CIpCam(%x) [%d] pszRGBData %x", this, i, m_stData[i].pszRGBData);
	}
	m_byIndex			= 0;
	m_byDispIndex		= 0;
	
	m_pFile = NULL;
	m_bIsRecording = FALSE;
	
	m_hThread			= NULL;
	m_bIsStop			= TRUE;
	
	m_hDispThread		= NULL;
	m_bIsDispStop		= TRUE;
	
	InitDecoder();
	
	m_bIsStopService	= 0;
	m_bIsStatusThreadEnd = FALSE;
	m_bIsDisplayThreadEnd = FALSE;
	LOGI("CIpCam::CIpCam(%x) is ok.", this);
}

CIpCam::~CIpCam()
{
	LOGI("CIpCam::~CIpCam(%x) begin...", this);
	StopService();
#if 1
	sem_destroy(&m_semDisplay);
	sem_destroy(&m_semaphore);
	h264UninitDecoder();
	delete m_szVideoData; m_szVideoData = NULL;
	delete m_szAudioData; m_szAudioData = NULL;
		
	sem_destroy(&m_semDisplay);
	for (int i=0; i<MAX_RGB16_DATA_NUM; i++)
	{
		delete m_stData[i].pszRGBData;
		m_stData[i].pszRGBData = NULL;
	}
#endif
	LOGI("CIpCam::~CIpCam(%x) end.", this);
}

void CIpCam::StopService()
{
	LOGI("CIpCam::StopService(%x) m_bIsStopService %d begin...", this, m_bIsStopService);
	if (!m_bIsStopService)
	{
		LOGI("CIpCam::StopService11(%x) begin...", this);
		
		do {
			if (!m_bIsCmdAttached)
			{
				break;
			}
			usleep(1);
		} while (1);
	
		sem_post(&m_semDisplay);
		StopDisplayThread();
		StopThread();
		LOGI("CIpCam::StopService(%x) stopThread() begin...", this);
		do {
			if (m_bIsDisplayThreadEnd && m_bIsStatusThreadEnd) {
				break;
			}
			usleep(1);
		} while (1);
#if 0
		sem_destroy(&m_semDisplay);
		sem_destroy(&m_semaphore);
		h264UninitDecoder();
		delete m_szVideoData; m_szVideoData = NULL;
		delete m_szAudioData; m_szAudioData = NULL;
			
		sem_destroy(&m_semDisplay);
		for (int i=0; i<MAX_RGB16_DATA_NUM; i++)
		{
			delete m_stData[i].pszRGBData;
			m_stData[i].pszRGBData = NULL;
		}
#endif
		m_bIsStopService = 1;
		LOGI("CIpCam::StopService(%x) end.", this);
	}
}

BOOL CIpCam::StartThread()
{
	m_bIsStop = FALSE;
	m_bIsStatusThreadEnd = FALSE;
	pthread_create(&m_hThread, 0, CheckStatusThread, this);
	
	return TRUE;
}

void CIpCam::StopThread()
{
	m_bIsStop = TRUE;
	m_hThread = NULL;
	LOGI("CIpCam::StopThread(%x) end.", this);
}

BOOL CIpCam::StartDisplayThread()
{
	m_bIsDispStop = FALSE;
	m_bIsStopService = FALSE;
	m_bIsDisplayThreadEnd = FALSE;
	pthread_create(&m_hDispThread, 0, DisplayThread, this);
	
	return TRUE;
}

void CIpCam::StopDisplayThread()
{
	m_bIsDispStop = TRUE;
	m_hDispThread = NULL;
}

static int s_iCount = 0;
void CIpCam::CheckStatus()
{
	int iCount = 0;
	LOGI("CIpCam::CheckStatus(%x) begin...", this);
	while (!m_bIsStop)
	{
		//LOGI("CIpCam::CheckStatus(%x) begin...", this);
	
		if (!m_oMdaClient.IsConnected() && m_bIsLogined)
		{
			if (m_bIsVideoOpened)
			{
				m_bIsVideoOpened = FALSE;
				LOGI("CIpCam::CheckStatus(%x) reconn count %d ", this, s_iCount++);
				m_oMdaClient.Close();
				LOGI("CIpCam::CheckStatus(%x) Close() is ok ", this);
			}
			if (time(NULL) - m_oMdaClient.GetDisconnTime() >= 5)
			{
				LOGI("CIpCam::CheckStatus(%x) RealPlay() ", this);
				RealPlay(m_bIsMainStream);
			}
		}
		if (m_bIsProcessing)
		{
			m_iWaitTime--;
			if (m_iWaitTime <= 0)
			{
				sem_post(&m_semaphore);
			}
		}
		iCount++;
		if (iCount >= 5)
		{
			if (m_bIsLogined)
			{
				if (m_iHBCount >= 6 || !m_oCmdClient.IsConnected())
				{
					m_bIsLogined = FALSE;
					m_oCmdClient.Close();
				}
				else
				{
					m_iHBCount++;
					HeartBeat();
				}
			}
			else
			{
				if (time(NULL) - m_oMdaClient.GetDisconnTime() >= 5)
				{
					m_bIsAutoLogin = TRUE;
					AutoLogin();
				}
			}
			iCount = 0;
		}
		//LOGI("CIpCam::CheckStatus(%x) end.", this);
		usleep(1000000);
	}
	m_bIsStatusThreadEnd = TRUE;
	LOGI("CIpCam::CheckStatus(%x) end.", this);
}

void CIpCam::ProcDisplay()
{
	jbyteArray 	jBuffer = NULL;
	int 		status, rgbDataLength;
    JNIEnv 		*env;
    jclass 		jclsProcess;
    jmethodID 	jmidProcess;
    BOOL 		bIsAttached = FALSE;
    
	status = m_poMdaJavaVM->GetEnv((void **)&env, JNI_VERSION_1_4);
    if (status < 0) {
        LOGI("ThreadEntrance(): failed to get JNI environment, assuming native thread");
        status = m_poMdaJavaVM->AttachCurrentThread(&env, NULL);
        if (status < 0) {
            LOGI("ThreadEntrance(): failed to attach current thread");
            return;
       	}
       
       	bIsAttached = TRUE;
   	}
   	
   	jclsProcess = env->GetObjectClass(m_objMdaInterface);
    if (jclsProcess == NULL)
    {
        LOGI("jclsProcess = NULL\n");
        return;
    }
    	
	jmidProcess = env->GetMethodID(jclsProcess, "myProcVideoData", "(III[B)V");  
	if (jmidProcess)  
	{
		m_pMdaEnv = env;
		m_jmidProcVideoData = jmidProcess;
		LOGI("myProcVideoData func is ok");
	}
		
	jmidProcess = env->GetMethodID(jclsProcess, "myProcAudioData", "(I[B)V");  
	if (jmidProcess)  
	{
		m_jmidProcAudioData = jmidProcess;
		LOGI("myProcAudioData func is ok");
	}

	m_bIsMdaAttached = TRUE;

	while (!m_bIsDispStop)
	{
#if 1
		if (m_byIndex == m_byDispIndex)
		{
			if (!m_stData[m_byIndex].byIsValid)
			{
				sem_wait(&m_semDisplay);
			}
		}
		//LOGI("CIpCam::ProcDisplay(%x) index %d valid %d begin...", this, m_byDispIndex, m_stData[m_byDispIndex].byIsValid);
		if (m_stData[m_byDispIndex].byIsValid && m_bIsCmdAttached)
		{
			rgbDataLength = m_stData[m_byDispIndex].nWidth * m_stData[m_byDispIndex].nHeight * 2;
			//LOGI("CIpCam::ProcDisplay(%x) NewByteArray(%d) begin...", this, rgbDataLength);
			jBuffer = m_pMdaEnv->NewByteArray(rgbDataLength);
			if (jBuffer)
			{
			//LOGI("CIpCam::ProcDisplay(%x) NewByteArray(%d) is ok", this, rgbDataLength);
				m_pMdaEnv->SetByteArrayRegion(jBuffer, 0, rgbDataLength, (jbyte *)m_stData[m_byDispIndex].pszRGBData);
			//LOGI("CIpCam::ProcDisplay(%x) SetByteArrayRegion(%d) is ok", this, rgbDataLength);
			
				m_pMdaEnv->CallVoidMethod((jclass)m_objMdaCallBack, m_jmidProcVideoData, m_stData[m_byDispIndex].nWidth, m_stData[m_byDispIndex].nHeight, rgbDataLength, jBuffer);
			//LOGI("CIpCam::ProcDisplay(%x) CallVoidMethod(%d) is ok", this, rgbDataLength);
				m_pMdaEnv->DeleteLocalRef(jBuffer);
			}
			
			//LOGI("CIpCam::ProcDisplay(%x) DeleteLocalRef(%d) is ok", this, rgbDataLength);
			m_stData[m_byDispIndex].byIsValid = 0;
			//LOGI("CIpCam::ProcDisplay(%x) index %d end.", this, m_byDispIndex);
			m_byDispIndex = (m_byDispIndex + 1) % MAX_RGB16_DATA_NUM;
		}
		else
		{
			usleep(1);
		}
#else
		usleep(1);
#endif
	}

	if (m_bIsMdaAttached)
	{
		m_poMdaJavaVM->DetachCurrentThread();
		LOGI("CIpCam::DetachJavaCallback(%x) mda DetachCurrentThread() is ok.", this);
		m_bIsMdaAttached = FALSE;
		m_pMdaEnv = NULL;
		m_jmidProcVideoData = NULL;
		m_jmidProcAudioData = NULL;
	}
	m_bIsDisplayThreadEnd = TRUE;
}

void *CIpCam::CheckStatusThread(void *pParam)
{
	CIpCam *poIpCam = (CIpCam *)pParam;
	if (poIpCam)
	{
		poIpCam->CheckStatus();
	}
	return NULL;
}

void *CIpCam::DisplayThread(void *pParam)
{
	CIpCam *poIpCam = (CIpCam *)pParam;
	if (poIpCam)
	{
		poIpCam->ProcDisplay();
	}
	return NULL;
}

void CIpCam::AutoLogin()
{
	BOOL			bRet = FALSE;
	REGISTERREQUEST	stRequest;
	
	if (!m_oCmdClient.IsConnected())
	{
		m_oCmdClient.Open(GetIpAddr(), m_iCmdPort);
		LOGI("CIpCam::AutoLogin() Open()");
	}

	if (!m_oCmdClient.IsConnected())
	{
		LOGI("CIpCam::AutoLogin() Open() failure");
		return;
	}
	
	memset(&stRequest, 0, sizeof(REGISTERREQUEST));
	stRequest.byDevType		= 2;
	stRequest.dwDeviceID	= 0;
	stRequest.byChanNum		= 0;
	memcpy(stRequest.szUserName, m_szUserName, 8);
	memcpy(stRequest.szPassword, m_szPassword, 8);
	m_oCmdClient.IRLAB_SendMessage(0, 0, CMD_REGISTER_REQUEST, 0, m_nSessionID, sizeof(REGISTERREQUEST), (const char *)&stRequest);
}

BOOL CIpCam::OnLogin(const char *pszIpAddr, int iPort1, int iPort2, const char *pszUserName, const char *pszPassword)
{
	BOOL			bRet = FALSE;
	REGISTERREQUEST	stRequest;
	
	if (m_bIsProcessing)
	{
		return FALSE;
	}
	
	SetIpAddr(pszIpAddr);
	SetCmdPort(iPort1);
	SetMediaPort(iPort2);
	SetUserName(pszUserName);
	SetPassword(pszPassword);
	
	for (int i=0; i<MAX_RGB16_DATA_NUM; i++)
	{
		m_stData[i].byIsValid = 0;
		m_stData[i].nWidth = 0;
		m_stData[i].nHeight = 0;
		m_stData[i].pszRGBData = new unsigned char[MAX_BUFFER_SIZE];
		memset(m_stData[i].pszRGBData, 0, MAX_BUFFER_SIZE);
	}
	m_byIndex			= 0;
	m_byDispIndex		= 0;
	
	if (!m_oCmdClient.IsConnected())
	{
		m_oCmdClient.Open(GetIpAddr(), m_iCmdPort);
		LOGI("CIpCam::OnLogin() Open()");
	}

	if (!m_oCmdClient.IsConnected())
	{
		m_bIsDisplayThreadEnd = TRUE; m_bIsStatusThreadEnd = TRUE;
		LOGI("CIpCam::OnLogin() Open() failure");
		return bRet;
	}
	
	m_iWaitTime		= 3;
	m_bIsLogined	= FALSE;
	m_bIsProcessing = TRUE;
	memset(&stRequest, 0, sizeof(REGISTERREQUEST));
	stRequest.byDevType		= 2;
	stRequest.dwDeviceID	= 0;
	stRequest.byChanNum		= 0;
	memcpy(stRequest.szUserName, m_szUserName, 8);
	memcpy(stRequest.szPassword, m_szPassword, 8);
	m_oCmdClient.IRLAB_SendMessage(0, 0, CMD_REGISTER_REQUEST, 0, m_nSessionID, sizeof(REGISTERREQUEST), (const char *)&stRequest);
	LOGI("CIpCam::OnLogin() SendMessage() loginRequest.");
	sem_wait(&m_semaphore);
	if (m_bIsLogined)
	{
		LOGI("CIpCam::OnLogin() SendMessage() loginRequest is OK.");
		m_iHBCount = 0;
		bRet = TRUE;
		StartThread();
		StartDisplayThread();
	}
	else
	{
		m_bIsDisplayThreadEnd = TRUE; m_bIsStatusThreadEnd = TRUE;
		LOGI("CIpCam::OnLogin() SendMessage() loginRequest is failure.");
	}
	
	m_bIsProcessing = FALSE;
	return bRet;
}

BOOL CIpCam::OnLogout()
{
	if (m_bIsLogined)
	{
		m_oCmdClient.IRLAB_SendMessage(0, 0, CMD_UNREGISTER_REQUEST, 0, m_nSessionID, 0, NULL);
	}
	
	m_oMdaClient.Close();
	m_oCmdClient.Close();
	StopThread();
	return TRUE;
}

void CIpCam::HeartBeat()
{
	if (m_bIsLogined)
	{
		m_oCmdClient.IRLAB_SendMessage(0, 0, CMD_HEART_BEAT_REQUEST, 0, m_nSessionID, 0, NULL);
	}
}

BOOL CIpCam::RealPlay(BOOL bIsMainStream)
{
	BOOL			bRet = FALSE;
	STREAMREQUEST	stRequest;

	if (m_bIsProcessing || !m_bIsLogined || m_bIsVideoOpened)
	{
		return bRet;
	}

	memset(&stRequest, 0, sizeof(STREAMREQUEST));
	stRequest.byChanID		= 0;
	stRequest.dwDeviceID	= 0;
	stRequest.byType		= bIsMainStream ? 0 : 1;
	stRequest.byStatus		= 1;
	stRequest.byTransProto	= 0;
	m_bIsProcessing			= TRUE;
	m_iWaitTime				= 3;
	m_oCmdClient.IRLAB_SendMessage(0, 0, STREAM_REQUEST, 0, m_nSessionID, sizeof(STREAMREQUEST), (const char *)&stRequest);
	sem_wait(&m_semaphore);
	if (m_nResult == 0)
	{
		m_bIsMainStream = bIsMainStream;
		m_bIsVideoOpened = TRUE;
		bRet = TRUE;
	}

	m_bIsProcessing = FALSE;
	return bRet;
}

BOOL CIpCam::StopPlay()
{
	BOOL			bRet = FALSE;
	STREAMREQUEST	stRequest;

	if (m_bIsProcessing || !m_bIsLogined || !m_bIsVideoOpened)
	{
		return bRet;
	}

	memset(&stRequest, 0, sizeof(STREAMREQUEST));
	stRequest.byChanID		= 0;
	stRequest.dwDeviceID	= 0;
	stRequest.byType		= m_bIsMainStream ? 0 : 1;
	stRequest.byStatus		= 0;
	stRequest.byTransProto	= 0;
	m_bIsProcessing			= TRUE;
	m_iWaitTime				= 3;
	m_oCmdClient.IRLAB_SendMessage(0, 0, STREAM_REQUEST, 0, m_nSessionID, sizeof(STREAMREQUEST), (const char *)&stRequest);
	sem_wait(&m_semaphore);
	if (m_nResult == 0)
	{
		m_bIsVideoOpened = FALSE;
		bRet = TRUE;
	}

	m_bIsProcessing = FALSE;
	return bRet;
}

BOOL CIpCam::OpenSound()
{
	BOOL			bRet = FALSE;
	STREAMREQUEST	stRequest;

	if (m_bIsProcessing || !m_bIsLogined || m_bIsAudioOpened)
	{
		return bRet;
	}

	memset(&stRequest, 0, sizeof(STREAMREQUEST));
	stRequest.byChanID		= 0;
	stRequest.dwDeviceID	= 0;
	stRequest.byType		= 2;
	stRequest.byStatus		= 1;
	stRequest.byTransProto	= 0;
	m_bIsProcessing			= TRUE;
	m_iWaitTime				= 3;
	m_oCmdClient.IRLAB_SendMessage(0, 0, STREAM_REQUEST, 0, m_nSessionID, sizeof(STREAMREQUEST), (const char *)&stRequest);
	sem_wait(&m_semaphore);
	if (m_nResult == 0)
	{
		m_bIsAudioOpened = TRUE;
		bRet = TRUE;
	}

	m_bIsProcessing = FALSE;
	
	return bRet;
}

BOOL CIpCam::CloseSound()
{
	BOOL			bRet = FALSE;
	STREAMREQUEST	stRequest;

	if (m_bIsProcessing || !m_bIsLogined || !m_bIsAudioOpened)
	{
		return bRet;
	}

	memset(&stRequest, 0, sizeof(STREAMREQUEST));
	stRequest.byChanID		= 0;
	stRequest.dwDeviceID	= 0;
	stRequest.byType		= 2;
	stRequest.byStatus		= 0;
	stRequest.byTransProto	= 0;
	m_bIsProcessing			= TRUE;
	m_iWaitTime				= 3;
	m_oCmdClient.IRLAB_SendMessage(0, 0, STREAM_REQUEST, 0, m_nSessionID, sizeof(STREAMREQUEST), (const char *)&stRequest);
	sem_wait(&m_semaphore);
	if (m_nResult == 0)
	{
		m_bIsAudioOpened = FALSE;
		bRet = TRUE;
	}

	m_bIsProcessing = FALSE;
	return bRet;
}

BOOL CIpCam::PTZControl(int nCommand, int nSpeed)
{
	BOOL		bRet = FALSE;
	PTZREQUEST	stRequest;

	if (m_bIsProcessing || !m_bIsLogined)
	{
		return bRet;
	}

	memset(&stRequest, 0, sizeof(PTZREQUEST));
	stRequest.byCommand		= (unsigned char)nCommand;
	stRequest.bySpeed0		= (unsigned char)nSpeed;
	stRequest.bySpeed1		= (unsigned char)nSpeed;
	m_bIsProcessing			= TRUE;
	m_iWaitTime				= 3;
	m_oCmdClient.IRLAB_SendMessage(0, 0, PTZ_CTRL_REQUEST, 0, m_nSessionID, sizeof(PTZREQUEST), (const char *)&stRequest);
	sem_wait(&m_semaphore);
	if (m_nResult == 0)
	{
		bRet = TRUE;
	}
	
	m_bIsProcessing = FALSE;
	return bRet;
}

BOOL CIpCam::StartRecord(const char *pszFileName)
{
	if (!pszFileName)
	{
		return FALSE;
	}
	
	m_pFile = fopen(pszFileName, "wb");
	if (m_pFile)
	{
		m_bIsRecording = TRUE;
	}
	
	return m_bIsRecording;
}

BOOL CIpCam::StopRecord()
{
	m_bIsRecording = FALSE;
	if (m_pFile)
	{
		fclose(m_pFile);
		m_pFile = NULL;
	}
	
	return TRUE;
}

void CIpCam::StreamNotify(int index)
{
	STREAMNOTIFY	stNotify;
	KEYFRAMEREQUEST	stRequest;

	LOGI("CIpCam::StreamNotify(%x) index %d begin...", this, index);

	if (!m_oMdaClient.IsConnected())
	{
		m_oMdaClient.Open(GetIpAddr(), m_iMdaPort);
	}

	if (!m_oMdaClient.IsConnected())
	{
		LOGI("CIpCam::StreamNotify(%x) index %d failure.", this, index);
		return;
	}

	m_bIsFirst = TRUE;
	stNotify.dwDeviceID	= index;
	m_oMdaClient.IRLAB_SendMessage(0, 0, STREAM_NOTIFY, 0, m_nSessionID, sizeof(STREAMNOTIFY), (const char *)&stNotify);

	stRequest.byChanID = index;
	m_oCmdClient.IRLAB_SendMessage(0, 0, KEY_FRAME_REQUEST, 0, m_nSessionID, sizeof(KEYFRAMEREQUEST), (const char *)&stNotify);
	LOGI("CIpCam::StreamNotify(%x) index %d end.", this, index);
}

void CIpCam::LoginResponse(unsigned short nSessionID, unsigned short nMessageLen, const char *pszMessage)
{
	PGENERALRESPONSE	pstResponse;

	LOGI("CIpCam::LoginResponse() begin...");
	if (pszMessage && nMessageLen >= sizeof(GENERALRESPONSE))
	{
		pstResponse = (PGENERALRESPONSE)pszMessage;
		if (pstResponse)
		{
			if (!pstResponse->nResult)
			{
				m_bIsLogined	= TRUE;
				m_nSessionID	= nSessionID;
				LOGI("CIpCam::LoginResponse() is ok.");
				if (m_bIsAutoLogin)
				{
					m_bIsAutoLogin = FALSE;
					return;
				}
			}
		}	
	}
	LOGI("CIpCam::LoginResponse() end.");
	sem_post(&m_semaphore);
}

void CIpCam::StreamResponse(unsigned short nSessionID, unsigned short nMessageLen, const char *pszMessage)
{
	PGENERALRESPONSE	pstResponse;
	
	LOGI("CIpCam::StreamResponse() begin...");
	if (pszMessage && nMessageLen >= sizeof(GENERALRESPONSE))
	{
		pstResponse = (PGENERALRESPONSE)pszMessage;
		if (pstResponse)
		{
			m_nResult = pstResponse->nResult;
			LOGI("CIpCam::StreamResponse() ret %d index %d.", m_nResult, pstResponse->nReserved);
			if (!pstResponse->nResult)
			{
				if (pstResponse->nReserved != 0xFFFF)
				{
					StreamNotify(pstResponse->nReserved);
				}
			}
		}
	}
	
	LOGI("CIpCam::StreamResponse() end.");
	sem_post(&m_semaphore);
}

void CIpCam::PTZCtrlResponse(unsigned short nSessionID, unsigned short nMessageLen, const char *pszMessage)
{
	PGENERALRESPONSE	pstResponse;

	LOGI("CIpCam::PTZCtrlResponse() begin...");
	if (pszMessage && nMessageLen >= sizeof(GENERALRESPONSE))
	{
		pstResponse = (PGENERALRESPONSE)pszMessage;
		if (pstResponse)
		{
			m_nResult = pstResponse->nResult;
		}
	}
	
	LOGI("CIpCam::PTZCtrlResponse() end.");
	sem_post(&m_semaphore);
}

static int s_iFrameCount = 0;
static time_t s_curTime = 0, s_begTime = 0;
void CIpCam::ProcVideodata(const char *pszMessage, unsigned short nMessageLen)
{
#if 0
	jbyteArray 			jBuffer;
	char				*pszData;
#endif
	int					rgbDataLength;
	char				*pszYUVData0, *pszYUVData1, *pszYUVData2;
	int					linesize0, linesize1, linesize2;
	int					width, height, iRet;
	unsigned char		byPackIndex, index;
	unsigned short		nDataLen;
	unsigned int		dwFrameLength, dwFrameNo;
	const char			*pAVData = pszMessage + sizeof(VIDEODATAHEAD);
	PVIDEODATAHEAD		pVideoHead = (PVIDEODATAHEAD)pszMessage;

	index				= pVideoHead->byChannelID;// - MAX_DEVICE_CHANNEL_NUM;
	byPackIndex			= pVideoHead->byPackIndex;
	nDataLen			= pVideoHead->nDataLength;
	dwFrameLength		= pVideoHead->dwFrameLength;
	dwFrameNo			= pVideoHead->dwFrameID;

	//LOGI("CIpCam::ProcVideodata(%x) index %d length %d m_szVideoData %x begin...", this, byPackIndex, nDataLen, m_szVideoData);

	if (index >= 4)
	{
		index -= 4;
	}
	
	if (index < 0 || index >= 4)
	{
		//LOGI("recv[%d] video data len %d \n", index, dwFrameLength);
		return;
	}

	if (byPackIndex == 0)
	{
		m_dwVideoFrameLen = 0;
	}
	memcpy(m_szVideoData + byPackIndex * MAX_PACKET_AV_DATA_LEN, pAVData, nDataLen);
	m_dwVideoFrameLen += nDataLen;
	//LOGI("recv video data[%d] len %d total %d\n", byPackIndex, nDataLen, dwFrameLength);
	if (m_dwVideoFrameLen == dwFrameLength)
	{
		if (m_bIsFirst && pVideoHead->byFrameType == 0x01)
		{
			return;
		}
		m_bIsFirst = FALSE;
		
		if (m_bIsVideoOpened)
		{
			if (m_bIsRecording && m_pFile)
			{
				fwrite(m_szVideoData, 1, m_dwVideoFrameLen, m_pFile);
			}
			
			iRet = h264DecoderNal(m_szVideoData, m_dwVideoFrameLen);
			//LOGI("CIpCam::ProcVideodata(%x) h264DecoderNal() ret %d.\n", this, iRet);
			if (iRet && m_jmidProcVideoData)
			{
				m_bIsDisplaying = TRUE;
				GetPicture(&width, &height, &linesize0, &linesize1, &linesize2, &pszYUVData0, &pszYUVData1, &pszYUVData2);

				rgbDataLength = width * height * 2;

				//LOGI("CIpCam::ProcVideodata(%x) m_byIndex %d begin...\n", this, m_byIndex);
				YV12ToRGB16((unsigned int*)m_stData[m_byIndex].pszRGBData, (unsigned char *)pszYUVData0, (unsigned char *)pszYUVData1, (unsigned char *)pszYUVData2, width, height, linesize0, linesize1, width);
				m_stData[m_byIndex].byIsValid = 1;
				m_stData[m_byIndex].nWidth = width;
				m_stData[m_byIndex].nHeight = height;
				//m_byDispIndex = m_byIndex;
				//LOGI("CIpCam::ProcVideodata(%x) m_byIndex %d end.\n", this, m_byIndex);
				m_byIndex = (m_byIndex + 1) % MAX_RGB16_DATA_NUM;
				sem_post(&m_semDisplay);

				m_bIsDisplaying = FALSE;
			}
			
			s_iFrameCount++;
			s_curTime = time(NULL);
			if (s_curTime - s_begTime >= 1)
			{
				LOGI("current Frame Rate %d\n", s_iFrameCount);
				s_begTime = s_curTime;
				s_iFrameCount = 0;
			}
			//LOGI("recv video data frameNo %d len %d total %d\n", dwFrameNo, dwFrameLength);
		}
	}
	
	//LOGI("CIpCam::ProcVideodata(%x) end.", this);
}

void CIpCam::ProcAudiodata(const char *pszMessage, unsigned short nMessageLen)
{
	unsigned char		byPackIndex, index;
	unsigned short		nDataLen;
	unsigned int		dwFrameLength, dwFrameNo;
	const char			*pAVData = pszMessage + sizeof(AUDIODATAHEAD);
	PAUDIODATAHEAD		pAudioHead = (PAUDIODATAHEAD)pszMessage;

	index				= pAudioHead->byChannelID;
	byPackIndex			= pAudioHead->byPackIndex;
	nDataLen			= pAudioHead->nDataLength;
	dwFrameLength		= pAudioHead->dwFrameLength;
	dwFrameNo			= pAudioHead->dwFrameID;

	if (index < 0 || index >= 4)
	{
		return;
	}
	
	
	if (byPackIndex == 0)
	{
		m_dwAudioFrameLen = 0;
	}

	memcpy(m_szAudioData + byPackIndex * MAX_PACKET_AV_DATA_LEN, pAVData, nDataLen);
	m_dwAudioFrameLen += nDataLen;
	LOGI("recv audio data[%d] len %d total %d\n", byPackIndex, nDataLen, dwFrameLength);
	if (m_dwAudioFrameLen == dwFrameLength)
	{
	
	}
}

void CIpCam::AttachJavaCallback(BOOL bIsCmdFunc)
{
	int 		status;
    JNIEnv 		*env;
    jclass 		jclsProcess;
    jmethodID 	jmidProcess;
    BOOL 		bIsAttached = FALSE;
    
    if (bIsCmdFunc)
	{
    	status = m_poCmdJavaVM->GetEnv((void **)&env, JNI_VERSION_1_4);
    	if (status < 0) {
        	LOGI("ThreadEntrance(): failed to get JNI environment, assuming native thread");
        	status = m_poCmdJavaVM->AttachCurrentThread(&env, NULL);
        	if (status < 0) {
            	LOGI("ThreadEntrance(): failed to attach current thread");
            	return;
       		}
       
       		bIsAttached = TRUE;
   		}
   	
   		jclsProcess = env->GetObjectClass(m_objCmdInterface);
    	if (jclsProcess == NULL)
    	{
        	LOGI("jclsProcess = NULL\n");
        	return;
    	}
    
		jmidProcess = env->GetMethodID(jclsProcess, "myProcMessage", "(II[B)I");  
		if (jmidProcess)  
		{
			m_pCmdEnv = env;
			m_jmidProcess = jmidProcess;
		}
		LOGI("myProcMessage func is ok");
		m_bIsCmdAttached = TRUE;
	}
#if 0
	else
	{
		status = m_poMdaJavaVM->GetEnv((void **)&env, JNI_VERSION_1_4);
    	if (status < 0) {
        	LOGI("ThreadEntrance(): failed to get JNI environment, assuming native thread");
        	status = m_poMdaJavaVM->AttachCurrentThread(&env, NULL);
        	if (status < 0) {
            	LOGI("ThreadEntrance(): failed to attach current thread");
            	return;
       		}
       
       		bIsAttached = TRUE;
   		}
   	
   		jclsProcess = env->GetObjectClass(m_objMdaInterface);
    	if (jclsProcess == NULL)
    	{
        	LOGI("jclsProcess = NULL\n");
        	return;
    	}
    	
		jmidProcess = env->GetMethodID(jclsProcess, "myProcVideoData", "(III[B)V");  
		if (jmidProcess)  
		{
			m_pMdaEnv = env;
			m_jmidProcVideoData = jmidProcess;
			LOGI("myProcVideoData func is ok");
		}
		
		jmidProcess = env->GetMethodID(jclsProcess, "myProcAudioData", "(I[B)V");  
		if (jmidProcess)  
		{
			m_jmidProcAudioData = jmidProcess;
			LOGI("myProcAudioData func is ok");
		}
		
		m_bIsMdaAttached = TRUE;
	}
#endif
}

void CIpCam::DetachJavaCallback(BOOL bIsCmdFunc)
{
	LOGI("CIpCam::DetachJavaCallback(%x) begin...", this);
	if (bIsCmdFunc)
	{
		if (m_bIsCmdAttached)
		{
			m_poCmdJavaVM->DetachCurrentThread();
			LOGI("CIpCam::DetachJavaCallback(%x) cmd DetachCurrentThread() is ok.", this);
			m_bIsCmdAttached = FALSE;
			m_pCmdEnv = NULL;
			m_jmidProcess = NULL;
		}
	}
#if 0
	else
	{
		if (m_bIsMdaAttached)
		{ 
			do {
				if (!m_bIsDisplaying)
				{
					break;
				}
				usleep(1);
			} while (1);
			m_poMdaJavaVM->DetachCurrentThread();
			LOGI("CIpCam::DetachJavaCallback(%x) mda DetachCurrentThread() is ok.", this);
			m_bIsMdaAttached = FALSE;
			m_pMdaEnv = NULL;
			m_jmidProcVideoData = NULL;
			m_jmidProcAudioData = NULL;
		}
	}
#endif
	LOGI("CIpCam::DetachJavaCallback(%x) end.", this);
}

void CIpCam::IRLAB_ProcMessage(
										int					iConnType,
										int					iServID,
										unsigned char		byEncryption,
										unsigned short		nCommand,
										unsigned int		dwResult,
										unsigned short		nSessionID,
										unsigned short		nMessageLen,
										const char			*pszMessage)
{
	/*
	if (CMD_HEART_BEAT_RESPONSE != nCommand)
	{
		LOGI("IRLAB_ProcMessage(%x) cmd %d nSessionID %d id %d begin...", this, nCommand, nSessionID, iServID);
	}
	*/
	m_iHBCount = 0;
	switch (nCommand)
	{
	case NOTIFY_CONN_STATUS:
		if (nSessionID == HAS_CONNECTED)
		{
			AttachJavaCallback(iServID == 1 ? 1 : 0);
		}
		else
		{
			LOGI("IRLAB_ProcMessage(%x) NOTIFY_CONN_STATUS disconnect iServID = %d", this, iServID);
			DetachJavaCallback(iServID == 1 ? 1 : 0);
		}
		break;

	case CMD_REGISTER_RESPONSE:
		LoginResponse(nSessionID, nMessageLen, pszMessage);
		break;
		
	case CMD_UNREGISTER_RESPONSE:
		break;
		
	case CMD_HEART_BEAT_RESPONSE:
		break;
		
	case STREAM_RESPONSE:
		StreamResponse(nSessionID, nMessageLen, pszMessage);
		break;
		
	case PTZ_CTRL_RESPONSE:
		PTZCtrlResponse(nSessionID, nMessageLen, pszMessage);
		break;
		
	case SEND_VIDEO_DATA:
		ProcVideodata(pszMessage, nMessageLen);
		break;

	case SEND_AUDIO_DATA:
		ProcAudiodata(pszMessage, nMessageLen);
		break;

	default:
		break;
	}
	/*
	if (CMD_HEART_BEAT_RESPONSE != nCommand)
	{
		LOGI("IRLAB_ProcMessage(%x) cmd %d nSessionID %d id %d end.", this, nCommand, nSessionID, iServID);
	}
	*/
}

void CIpCam::IRLAB_ClientRecvData(
										int					iConnType,
										int					iServID,
										unsigned char		byEncryption,
										unsigned short		nCommand,
										unsigned int		dwResult,
										unsigned short		nSessionID,
										unsigned short		nMessageLen,
										const char			*pszMessage,
										void				*pContext)
{
	CIpCam *poIpCam = (CIpCam *)pContext;
	if (poIpCam)
	{
		poIpCam->IRLAB_ProcMessage(iConnType, iServID, byEncryption, nCommand, dwResult, nSessionID, nMessageLen, pszMessage);
	}
}

const char *CIpCam::GetIpAddr()
{
	static char 	szIpAddr[20];
	struct hostent	*host = NULL;
	
	host = gethostbyname(m_szIpAddr);
	if (host)
	{
		sprintf(szIpAddr, "%d.%d.%d.%d",
					(host->h_addr_list[0][0]&0x00ff),
					(host->h_addr_list[0][1]&0x00ff),
					(host->h_addr_list[0][2]&0x00ff),
					(host->h_addr_list[0][3]&0x00ff));
	}
	else
	{
		strcpy(szIpAddr, m_szIpAddr);
	}
	
	return szIpAddr;
}

void CIpCam::SetIpAddr(const char *pszIpAddr)
{
	if (!pszIpAddr)
	{
		return;
	}
	
	memset(m_szIpAddr, 0, sizeof(m_szIpAddr));
	if (strlen(pszIpAddr) >= sizeof(m_szIpAddr))
	{
		memcpy(m_szIpAddr, pszIpAddr, sizeof(m_szIpAddr) - 1);
	}
	else
	{
		strcpy(m_szIpAddr, pszIpAddr);
	}
}

int CIpCam::GetCmdPort()
{
	return m_iCmdPort;
}

void CIpCam::SetCmdPort(int iPort)
{
	m_iCmdPort = iPort;
}
	
int CIpCam::GetMediaPort()
{
	return m_iMdaPort;
}

void CIpCam::SetMediaPort(int iPort)
{
	m_iMdaPort = iPort;
}
	
const char *CIpCam::GetUserName()
{
	return m_szUserName;
}

void CIpCam::SetUserName(const char *pszUserName)
{
	if (!pszUserName)
	{
		return;
	}
	
	memset(m_szUserName, 0, sizeof(m_szUserName));
	if (strlen(pszUserName) >= sizeof(m_szUserName))
	{
		memcpy(m_szUserName, pszUserName, sizeof(m_szUserName) - 1);
	}
	else
	{
		strcpy(m_szUserName, pszUserName);
	}
}
	
const char *CIpCam::GetPassword()
{
	return m_szPassword;
}

void CIpCam::SetPassword(const char *pszPassword)
{
	if (!pszPassword)
	{
		return;
	}
	
	memset(m_szPassword, 0, sizeof(m_szPassword));
	if (strlen(pszPassword) >= sizeof(m_szPassword))
	{
		memcpy(m_szPassword, pszPassword, sizeof(m_szPassword) - 1);
	}
	else
	{
		strcpy(m_szPassword, pszPassword);
	}
}

BOOL CIpCam::InitDecoder()
{
	return h264InitDecoder();
}

BOOL CIpCam::UninitDecoder()
{
	return h264UninitDecoder();
}

BOOL CIpCam::DecoderNal(unsigned char *pszVideoData, unsigned int dwFrameLength)
{
	return h264DecoderNal(pszVideoData, dwFrameLength);
}

